package dk.dtu.is.jjmvcp.service;

import java.util.Observable;

import dk.dtu.is.jjmvcp.controller.MyController;
import dk.dtu.is.jjmvcp.model.MyJavaBeanModel;

/**
 * The class represents the service layer through which the data of the <i>model</i> will be retrieved from. </br>
 * The model is the {@link MyJavaBeanModel MyJavaBeanModel} class. </br></br>
 * 
 * The logic to get such data could use different protocols and end points. Some could be: </br>
 * - REST Server </br>
 * - SOAP Server </br>
 * - any external server with any interface (XML, Socket, etc) </br>
 * - local file with relevant data </br></br>
 * 
 * In theory, this class could be expanded for different communication protocols, for instance for external serves (see list above) </br></br>
 * 
 * @author dsac
 *
 */
public class RandomService extends Observable implements iService, Runnable {
	
	/**
	 * A simple flag to keep track of whether the connection has been created with the remote service 
	 * (for instance, a server, a local file, a database).</br>
	 * In this prototype, we simulate it as there is no need to connect to any of these devices.
	 */
	private boolean isConnectionOpen = false;
	
	/**
	 * It is in charge of running the polling loop to read the {@link MyJavaBeanModel} from the remote location.
	 */
	private Thread pollingThread;
	/**
	 * How often the {@link #pollingThread} reads data from the remote service.</br>
	 * 5 seconds is the default value.
	 * {@value 5000}
	 */
	private long pollingPeriod = 5000;
	/**
	 * A simple flag to indicate whether the polling is active or not.</br>
	 * By "the polling is active" we intend that the {@link #pollingThread} is 
	 * alive and is retrieving data from the remote location.
	 */
	private boolean isPollingRunning = false;
	 
	

	/**
	 * Default constructor.
	 */
	public RandomService() {
		super();
	}

	/**
	 * Read how often the {@link #pollingThread} retrieves data from the remote location when the polling is active.
	 * @see dk.dtu.is.jjmvcp.service.iService#getPollingPeriod()
	 */
	@Override
	public long getPollingPeriod() {
		return pollingPeriod;
	}

	
	/**
	 * Set how often the {@link #pollingThread} retrieves data from the remote location when the polling is active.
	 * @see dk.dtu.is.jjmvcp.service.iService#getPollingPeriod()
	 */
	@Override
	public void setPollingPeriod(long milliseconds) {
		this.pollingPeriod = milliseconds;
	}


	/**
	 * It contains the necessary code to connect to the remote service in question.
	 * <p>
	 * In this prototype, as we run a random service, there is no need to embed code in this
	 * method. Should we connect to a RS232 port or a remote server which needs authentication, 
	 * then the necessary code should be place here. 
	 * </p>
	 * 
	 * @see dk.dtu.is.jjmvcp.service.iService#openConnection()
	 */
	@Override
	public void openConnection() {
		// Add potential code to connect to the data source, maybe a server, a socket or other services
		// In this example, there is no openConnection code
		
		isConnectionOpen = true;
	}


	/**
	 * It contains the necessary code to close the connection to the remote service in question.</br>
	 * This allows to free up system resources and do not block ports and/or services to other applications.
	 * <p>
	 * In this prototype, as we run a random service, there is no need to embed code in this
	 * method. Should we connect to a RS232 port or a remote server which needs authentication, 
	 * then the necessary code to disconnect from them should be place here. 
	 * </p>
	 * 
	 * @see dk.dtu.is.jjmvcp.service.iService#openConnection()
	 */
	@Override
	public void closeConnection() {
		// Add potential code to close the connection to the data source, maybe a server, a socket or other services
		// In this example, there is no closeConnection code
				
		isConnectionOpen = false;
	}


	/**
	 * It safely begins to poll the remote location for new data which is a {@link MyJavaBeanModel}.
	 * <p>
	 * This method takes care of making sure that the remote connection is properly connected
	 * and that the {@link #pollingThread} is properly running and functioning.
	 * </p>
	 * @see dk.dtu.is.jjmvcp.service.iService#startPolling()
	 */
	@Override
	public void startPolling() {
		// make sure that I start the polling properly.
		// if the poll thread is running, than I stop it and cancel any current instance.
		if (pollingThread!=null) {
			if (pollingThread.isAlive()) {
				pollingThread.interrupt();
			}
			pollingThread = null;
		}
		
		// create the thread and run the pollingTask in that Thread
		// make first sure that the connection is open:
		if (!(isConnectionOpen)) {
			openConnection();
		}
		// the Thread starts up and runs the *run* method in "this" class
		// In fact, this class implements the runnable method. 
		pollingThread = new Thread(this);
		pollingThread.start();
	}

	/**
	 * It safely ends to poll the remote service. 
	 * <p>
	 * This is achieved by making sure that the {@link #pollingThread} is safely handled.
	 * </p>
	 * 
	 * @see dk.dtu.is.jjmvcp.service.iService#stopPolling()
	 */
	@Override
	public void stopPolling() {
		if (pollingThread!=null) {
			pollingThread.interrupt();
			isPollingRunning = false;
			closeConnection();
		}
	}
	
	/**
	 * It retrieves new data from the remote service <b>ONLY ONCE</b>.
	 * <p>
	 * N.B. As it can be easily noted, the method does not return any object. </br>
	 * So one could ask: where would I get this new data? </br>
	 * such new data will be notified to the obserser, which is {@link MyController}, 
	 * as we invoke {@link #notifyObservers(Object)} in this method. </br>
	 * Thus, we new bean will be avaulable in the observer in the {@link MyController#update(Observable, Object)} method.
	 * </p>
	 * 
	 * @see MyController
	 */
	public void getMyJavaBean() 
	{
		// if this method gets called while the polling thread is running, then I 
		// quit the polling Thread
		if (isPollingRunning) {
			stopPolling();
		}
		// request my Bean
		openConnection();
		getBeanFromServiceAndNotify();
		closeConnection();
	}
	
	/**
	 * The actual method being run by the {@link RandomService#pollingThread}.
	 * 
	 * @see java.lang.Runnable#run()
	 */
	@Override
	public void run() {
		isPollingRunning = true;
		
		// make sure the connection is open
		// I open it only once when I am polling
		openConnection();
		
		// start the polling loop with a delay of "pollingPeriod"
    	while (isPollingRunning) {
    		try  {
    			getBeanFromServiceAndNotify();
    		} // try
	    	catch (Exception e) { //global try block
	    		e.printStackTrace();
	    		isPollingRunning = false;
	    	}
    		try {
				Thread.sleep(pollingPeriod);
			} catch (InterruptedException e) {
				// this block is executed when I interrupt the thread while it is sleeping
				//e.printStackTrace();
			}
    	} //while running
    	
    	// close the connection when I am done with the run()
    	// In this case, this should not occur as it is a continuous polling.
    	closeConnection();
	} //run method
	
	
	/**
	 * A simple wrapping method which incorporate {@link #getBeanFromService()} and add the 
	 * necessary calls to notify the observers that the bean has actually been retrieved.
	 * <p> 
	 * This is used in other parts of the code, especially in the: </br>
	 * - {@link #run()} method, which is started by {@link #startPolling()}. </br>
	 * - {@link #getMyJavaBean()} method, which is used for retrieving the java bean only once, without polling.
	 * </p> 
	 */
	private void getBeanFromServiceAndNotify()
	{
		// I call the my private method to actually retrieve the value from the remote service and
		// I notify the observers that the value I am interested in has changed
		// and I attach the bean to the notify method. By doing so, it will be 
		// readable by the controller, which is the observer of this Service Layer class.
		setChanged();
		notifyObservers(getBeanFromService());
	}
	
	/**
	 * Gets the model values - The model is the MyJavaBeanModel in this example.
	 * In this example, we get it from a random number generator. </br></br>
	 * 
	 * However, this could be implemented in many ways. The myJavaBeanModel values could actually be
	 * retrieved from any service, for instance a Web resource, an online server, or a local 
	 * text file. </br></br>
	 * 
	 * Knowing where the data comes from is not relevant, as long as we know that the logic 
	 * to access that information is stored in this class, and , more precisely, in this 
	 * particular method, which could potentially call external libraries, methods and other settings.
	 * 
	 * @return the updated instance of the MyJavaBeanModel 
	 */
	private MyJavaBeanModel getBeanFromService() {
		MyJavaBeanModel myJavaBeanModel = new MyJavaBeanModel();
		
		// create the new data for MyJavaBeanModel. Only one value is retrieved
		// here all the logic should be place to retrieve the value from any location
		int newValue = (int) Math.round(100*Math.random());
		
		// set the current new values of the javabean
		// N.B. as there are no listeners on this, no events will be generated.
		myJavaBeanModel.setMyValueOne(newValue);
		
		return myJavaBeanModel;
	}
}
